package com._101aiot.auth.config;

import com._101aiot.auth.service.impl.UserServiceImpl;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;

import javax.sql.DataSource;
import java.security.KeyPair;
import java.util.ArrayList;
import java.util.List;

/**
 * 认证服务配置中心
 */
@AllArgsConstructor
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {

    private final DataSource dataSource;

    private final UserServiceImpl userDetailsService;

    private final AuthenticationManager authenticationManager;

    private final JwtTokenEnhancer jwtTokenEnhancer;

//    private final PasswordEncoder passwordEncoder;

    /**
     * 直接调用数据库的 oauth_client_details 表的 client_id client_secret 通过认证才能账号密码访问
     *
     * @param clients 客户端
     * @throws Exception 异常
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 请求数据库库，校验client_id client_secret
        clients.jdbc(this.dataSource).clients(this.clientDetails());

        //
//        clients.inMemory()
//                .withClient("myApp")//客户端id
//                .secret(passwordEncoder.encode("myApp"))//密码，要保密
//                .accessTokenValiditySeconds(360)//访问令牌有效期
//                .refreshTokenValiditySeconds(360)//刷新令牌有效期
//                //授权客户端请求认证服务的类型authorization_code：根据授权码生成令牌，
//                // client_credentials:客户端认证，refresh_token：刷新令牌，password：密码方式认证
//                .authorizedGrantTypes("authorization_code", "client_credentials", "refresh_token", "password")
//                .scopes("all");//客户端范围，名称自定义，必填
    }

    @SuppressWarnings("all")
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> delegates = new ArrayList<>();
        delegates.add(jwtTokenEnhancer);
        delegates.add(accessTokenConverter());
        enhancerChain.setTokenEnhancers(delegates); // 配置JWT的内容增强器
        endpoints.authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService) // 配置加载用户信息的服务
                .accessTokenConverter(accessTokenConverter())
                .tokenEnhancer(enhancerChain);
    }

    /**
     * 访问令牌转换器
     *
     * @return JwtAccessTokenConverter
     */
    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setKeyPair(keyPair());
        return jwtAccessTokenConverter;
    }

    /**
     * 授权服务器的安全配置
     *
     * @param oauthServer 授权记录
     * @throws Exception 异常
     */
    @SuppressWarnings("all")
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer
                // 允许表单认证请求
                .allowFormAuthenticationForClients()
                // 密码加密方式
                .passwordEncoder(new BCryptPasswordEncoder())
                // 放行令牌密钥访问
                .tokenKeyAccess("permitAll()")
                // 放行校验令牌
                .checkTokenAccess("permitAll()");
    }

    /**
     * 客户端配置
     *
     * @return ClientDetailsService
     */
    @Bean
    public ClientDetailsService clientDetails() {

        return new JdbcClientDetailsService(dataSource);
    }


    /**
     * 密钥对
     *
     * @return KeyPair
     */
    @Bean
    public KeyPair keyPair() {
        // 从classpath下的证书中获取秘钥对
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "101aiot.com".toCharArray());
        return keyStoreKeyFactory.getKeyPair("jwt", "101aiot.com".toCharArray());
    }

}
